package com.visionet.jumper.core.exception;

import com.visionet.jumper.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.*;

/**
 * Created by TC.Ubuntu on 2017/1/12.
 * 全局统一异常处理
 */
public class ExceptionHandlerMethod extends ExceptionHandlerExceptionResolver {

    private static final Logger logger = LoggerFactory.getLogger(ExceptionHandlerMethod.class);

    @Override
    protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response,
                                                           HandlerMethod handlerMethod, Exception exception) {
        //异常日志输出
        StringWriter sw = null;
        PrintWriter pw = null;
        try {
            sw = new StringWriter();
            pw =  new PrintWriter(sw);
            //将出错的栈信息输出到printWriter中
            exception.printStackTrace(pw);
            pw.flush();
            sw.flush();
        } finally {
            if (sw != null) {
                try {
                    sw.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            if (pw != null) {
                pw.close();
            }
        }
        logger.error(sw.toString());

        //获取抛出异常的方法
        Method method = handlerMethod.getMethod();
        if(method == null) {
            return null;
        }

        ModelAndView resolveModelAndView = super.doResolveHandlerMethodException(request, response, handlerMethod, exception);
        ResponseBody responseBody = AnnotationUtils.findAnnotation(method, ResponseBody.class);
        if(responseBody != null) {
            try {
                ResponseStatus responseStatus = AnnotationUtils.findAnnotation(method, ResponseStatus.class);
                //请求响应，返回状态码或状态原因
                if(responseStatus != null) {
                    HttpStatus httpStatus = responseStatus.code();
                    String responseReason = responseStatus.reason();
                    if(StringUtils.isNotBlank(responseReason)) {
                        response.sendError(httpStatus.value(), responseReason);
                    } else {
                        response.setStatus(httpStatus.value());
                    }
                }
                //没有找到@ExceptionHandler处理异常，resolveModelAndView 为空
                if(resolveModelAndView == null) {
                    Map<String, Object> model = new HashMap<String, Object>();
                    model.put("msg", "操作异常:"+exception.getClass().getName() + "["+exception.getMessage()+"]");
                    model.put("success", false);
                   return this.handleResponseBody(model, null, request, response);
                } else {
                    resolveModelAndView.addObject("Msg", "系统异常：" + exception.getClass().getName() + "["+exception.getMessage()+"]");
                    resolveModelAndView.addObject("Success", "false");
                    return this.handleResponseBody(null, resolveModelAndView, request, response);
                }
            } catch (Exception e) {
                return null;
            }
        } else {
            if (resolveModelAndView == null) {
                resolveModelAndView = new ModelAndView();
            }
            resolveModelAndView.addObject("Msg", "系统异常：" + exception.getClass().getName() + "["+exception.getMessage()+"]");
            resolveModelAndView.addObject("exceptionDetail", sw.toString());
            resolveModelAndView.addObject("Success", "false");
            resolveModelAndView.setViewName("errorPage");
            return resolveModelAndView;
        }
    }

    private ModelAndView handleResponseBody(Map<String, Object> model, ModelAndView returnValue,
                                            HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        Map value = null;
        if(returnValue != null) {
            value = returnValue.getModelMap();
        } else {
            value = model;
        }
        HttpInputMessage inputMessage = new ServletServerHttpRequest(request);
        List<MediaType> acceptedMediaTypes = inputMessage.getHeaders().getAccept();
        if (acceptedMediaTypes.isEmpty()) {
            acceptedMediaTypes = Collections.singletonList(MediaType.ALL);
        }
        MediaType.sortByQualityValue(acceptedMediaTypes);
        HttpOutputMessage outputMessage = new ServletServerHttpResponse(response);
        Class<?> returnValueType = value.getClass();
        List<HttpMessageConverter<?>> messageConverters = super.getMessageConverters();
        if (messageConverters != null) {
            for (MediaType acceptedMediaType : acceptedMediaTypes) {
                for (HttpMessageConverter messageConverter : messageConverters) {
                    if (messageConverter.canWrite(returnValueType, acceptedMediaType)) {
                        messageConverter.write(value, acceptedMediaType, outputMessage);
                        return new ModelAndView();
                    }
                }
            }
        }
        return null;
    }
}
